package com.nts.visionworks.core.security.service;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.nts.visionworks.core.hibernate.transform.AutoCastableResultTransformer;
import com.nts.visionworks.core.security.domain.FunctionDefinitions;
import com.nts.visionworks.core.security.domain.ManagementDepartment;
import com.nts.visionworks.core.security.domain.ManagementMember;
import com.nts.visionworks.core.security.domain.SecurityAccessTrace;
import com.nts.visionworks.core.security.domain.SecurityAccessTraceId;
import com.nts.visionworks.core.security.domain.SecurityAuthority;
import com.nts.visionworks.core.security.domain.SecurityAuthorityFunction;
import com.nts.visionworks.core.security.domain.SecurityAuthorityFunctionId;
import com.nts.visionworks.core.security.domain.SecurityGroup;
import com.nts.visionworks.core.security.domain.SecurityIdentity;

/**
 * 보안 계정, 보안 그룹, 관리자, 관리 부서에 대한 DAO를 제공한다.
 * @author Devcken
 */
@Service("securityService")
@Transactional
public class SecurityService
{
	/**
	 * 보안과 관련된 {@link org.hibernate.SessionFactory}를 주입받는다.
	 */
	@Resource(name="visionworksSecuritySessionFactory")
	private SessionFactory _sessionFactory;
	
	/**
	 * 보안 계정 목록을 조회한다.
	 * @param enabled 보안 계정 사용 가능 여부에 대한 검색 조건을 설정한다.
	 * @param locked  보안 계정 잠금 여부에 대한 검색 조건을 설정한다.
	 * @param expired 보안 계정 만료 여부에 대한 검색 조건을 설정한다.
	 * @return 조회된 계정 목록이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<SecurityIdentity> listSecurityIdentities(boolean enabled, boolean locked, boolean expired)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityIdentity.class)
					.add(Restrictions.eq("enabled", enabled))
					.add(Restrictions.eq("locked", locked))
					.add(Restrictions.eq("expired", expired));
		
			List<SecurityIdentity> identities = criteria.list();
			
			return identities;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정 정보를 열람한다.
	 * @param securityId 보안 계정 아이디를 설정한다.
	 * @return 설정된 보안 계정 아이디에 대한 보안 계정 정보가 반환된다. 설정된 보안 계정 아이디에 해당되는 보안 계정 정보가 존재하지 않는 경우 null이 반환된다.
	 */
	public SecurityIdentity viewSecurityIdentity(String securityId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityIdentity.class);
		
			SecurityIdentity identity = (SecurityIdentity)criteria.add(Restrictions.eq("securityId", securityId)).uniqueResult();
			
			return identity;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정 정보를 새로 추가한다.
	 * @param securityId 보안 계정 아이디를 설정한다.
	 * @param securityPassword 보안 계정 암호를 설정한다.
	 * @param groupId 보안 계정이 속할 보안 그룹 아이디를 설정한다.
	 * @param authorityKey 보안 계정의 보안 권한 키를 설정한다.
	 * @param membersId 보안 계정에 속하는 관리자 아이디 목록을 설정한다.
	 * @return 새로 추가된 보안 계정의 보안 계정 정보가 반환된다.
	 */
	public SecurityIdentity insertSecurityIdentity(String securityId, String securityPassword, Integer groupId, String authorityKey, Integer[] membersId)
	{
		Session session = this._sessionFactory.openSession();
		
		Transaction transaction = session.beginTransaction();
		
		try
		{
			Criteria criteriaGroup = session.createCriteria(SecurityGroup.class)
					.add(Restrictions.eq("groupId", groupId));
			
			SecurityGroup group = (SecurityGroup)criteriaGroup.uniqueResult();
			
			Criteria criteriaAuthority = session.createCriteria(SecurityAuthority.class)
					.add(Restrictions.eq("authorityKey", authorityKey));
			
			SecurityAuthority authority = (SecurityAuthority)criteriaAuthority.uniqueResult();
			
			SecurityIdentity identity = new SecurityIdentity(authority, securityId, securityPassword, false, false, false, true, new Date());
			
			identity.getSecurityGroups().add(group);
			
			for (int index = 0; index < membersId.length; index++)
			{
				Integer memberId = membersId[index];
				
				Criteria criteriaMember = session.createCriteria(ManagementMember.class)
						.add(Restrictions.eq("memberId", memberId));
				
				ManagementMember member = (ManagementMember)criteriaMember.uniqueResult();
				
				identity.getManagementMembers().add(member);
			}
			
			// 보안 계정 정보를 데이터베이스에 새로 추가한다.
			session.save(identity);
			
			transaction.commit();
			
			return identity;
		}
		catch (Exception error)
		{			
			transaction.rollback();
			
			throw error;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정 정보를 수정한다.
	 * @param identityId 수정하고자 하는 보안 계정 아이디를 설정한다.
	 * @param membersId 보안 계정에 속하는 관리자 아이디 목록을 설정한다.
	 * @return 보안 계정이 올바르게 수정되면 true, 그렇지 않으면 false가 반환된다.
	 */
	public boolean updateSecurityIdentity(Long identityId, Integer[] membersId)
	{
		Session session = this._sessionFactory.openSession();
		
		Transaction transaction = session.beginTransaction();
		
		try
		{
			Criteria identityCriteria = session.createCriteria(SecurityIdentity.class)
				.add(Restrictions.eq("identityId", identityId));
			
			SecurityIdentity identity = (SecurityIdentity)identityCriteria.uniqueResult();
			
			if (identity == null) return false;
			
			// 보안 계정 아이디에 해당되는 모든 관리자를 제거한다.
			/*Query query = session.getNamedQuery("SecurityIdentityMember.DeleteByIdentityId")
				.setLong("identityId", identityId);
			
			query.executeUpdate();*/
			
			for (int index = 0; index < membersId.length; index++)
			{
				Integer memberId = membersId[index];
				
				Criteria memberCriteria = session.createCriteria(ManagementMember.class)
						.add(Restrictions.eq("memberId", memberId));
				
				ManagementMember member = (ManagementMember)memberCriteria.uniqueResult();
				
				identity.getManagementMembers().add(member);
			}
			
			session.save(identity);
			
			transaction.commit();
			
			return true;
		}
		catch (Exception error)
		{			
			transaction.rollback();
			
			throw error;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정을 삭제하기 위해 Enabled 프로퍼티를 설정한다.
	 * @param identity 삭제하고자 하는 보안 계정 정보를 설정한다.
	 */
	public void deleteSecurityIdentity(SecurityIdentity identity)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			identity.setEnabled(true);
			
			session.update(identity);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정을 잠그기 위해 Locked 프로퍼티를 설정한다.
	 * @param identity 잠그고자 하는 보안 계정 정보를 설정한다.
	 */
	public void lockSecurityIdentity(SecurityIdentity identity)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			identity.setLocked(true);
			
			session.update(identity);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정을 만료시키기 위해 Expired 프로퍼티를 설정한다.
	 * @param identity 만료시키고자 하는 보안 계정 정보를 설정한다.
	 */
	public void expireSecurityIdentity(SecurityIdentity identity)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			identity.setExpired(true);
			
			session.update(identity);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 보안 계정 아이디와 동일한 아이디의 보안 계정이 존재하는지를 열람한다. 
	 * @param securityId 검사할 보안 계정 아이디를 설정한다.
	 * @return 중복되는 보안 계정이 존재하지 않으면 true, 그렇지 않으면 false가 반환된다.
	 */
	public boolean checkDuplicationForSecurityId(String securityId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityIdentity.class)
					.add(Restrictions.eq("securityId", securityId));
			
			return criteria.uniqueResult() == null;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 그룹을 조회한다.
	 * @param startLikeHierachicalLabel 계층 라벨에 대해 검색하기 위해 검색 파라메터를 설정한다.
	 * @return 조회된 보안 그룹이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<SecurityGroup> listSecurityGroups(String startLikeHierachicalLabel)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			// ListSecurityGroupHierachically 프로시저를 실행하기 위해 NamedQuery를 가져온다. 프로시저 쿼리 결과와 SecurityGroupHierachy 클래스를 매핑하기 위해 ResultTransformer의 구현을 설정한다.
			Query query = session.getNamedQuery("SecurityGroup.ListSecurityGroupHierachically")
					.setString("StartLikeHierachicalLabel", startLikeHierachicalLabel)
					.setResultTransformer(new AutoCastableResultTransformer<SecurityGroup>(SecurityGroup.class)); 
			
			return query.list();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 그룹을 조회한다.
	 * @return 조회된 보안 그룹이 반환된다.
	 */
	public List<SecurityGroup> listSecurityGroups()
	{
		return this.listSecurityGroups("");
	}
	
	/**
	 * 보안 그룹 정보를 저장한다.
	 * @param group 저장할 보안 그룹 정보를 설정한다.
	 */
	public void saveSecurityGroup(SecurityGroup group)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			session.saveOrUpdate(group);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 그룹 정보를 삭제한다.
	 * @param group 삭제한 보안 그룹 정보를 설정한다.
	 */
	public void deleteSecurityGroup(Integer groupId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			// 삭제하려는 보안 그룹의 하위 보안 그룹까지 모두 삭제하기 위한 프로시저를 실행한다.ㄴ
			Query query = session.getNamedQuery("SecurityGroup.DeleteSecurityGroup")
					.setInteger("GroupId", groupId);
			
			query.executeUpdate();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 계정을 보안 그룹에 소속시키기 위해 계정 아이디와 그룹 아이디를 보안 계정 그룹에 추가한다.
	 * @param identityId 그룹에 소속시킬 보안 계정 아이디를 설정한다.
	 * @param groupId 보안 계정을 소속시킬 그룹 아이디를 설정한다.
	 */
	public void insertIdentityToGroup(Long identityId, Integer groupId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			SecurityGroup group = new SecurityGroup();
			
			group.setGroupId(groupId);
			
			Criteria identityCriteria = session.createCriteria(SecurityIdentity.class)
					.add(Restrictions.eq("identityId", identityId));
			
			SecurityIdentity identity = (SecurityIdentity)identityCriteria.uniqueResult();
			
			identity.getSecurityGroups().add(group);
			
			session.save(identity);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 권한 목록을 조회한다.
	 * @return 조회된 보안 권한 목록이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<SecurityAuthority> listSecurityAuthorities()
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityAuthority.class);
			
			return criteria.list();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 권한 정보를 열람한다.
	 * @param authorityKey 열람하고자 하는 보안 권한의 키를 설정한다.
	 * @return 설정된 보안 권한 키에 대한 보안 권한 정보가 반환된다. 설정된 보안 권한 키에 대한 보안 권한 정보가 존재하지 않을 경우 null이 반환된다.
	 */
	public SecurityAuthority viewSecurityAuthority(String authorityKey)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityAuthority.class);
			
			SecurityAuthority authority = (SecurityAuthority)criteria.add(Restrictions.eq("authorityKey", authorityKey)).uniqueResult();
			
			return authority;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 권한 기능 목록을 조회한다.
	 * @param authorityKey 조회하려는 보안 권한 기능에 대한 보안 권한 키를 설정한다.
	 * @return 설정된 보안 권한 키에 대한 보안 권한 기능 목록이 반환된다. 
	 */
	@SuppressWarnings("unchecked")
	public List<SecurityAuthorityFunction> listSecurityAuthorityFunctions(String authorityKey)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityAuthorityFunction.class);
			
			criteria.add(Restrictions.eq("authorityKey", authorityKey));
			
			return criteria.list();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 권한 기능 정보를 열람한다.
	 * @param functionKey 열람하고자 하는 보안 권한 기능에 대한 기능 정의 키를 설정한다.
	 * @param authorityKey 열람하고자 하는 보안 권한 기능에 대한 보안 권한 키를 설정한다.
	 * @return 설정된 기능 정의 키와 보안 권한 키에 대한 보안 권한 기능 정보가 반환된다.
	 */
	public SecurityAuthorityFunction viewSecurityAuthorityFunction(String functionKey, String authorityKey)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityAuthorityFunction.class)
					.add(Restrictions.eq("functionKey", functionKey))
					.add(Restrictions.eq("authorityKey", authorityKey));
			
			return (SecurityAuthorityFunction)criteria.uniqueResult();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 권한 기능을 추가한다.
	 * @param functionKey 권한 기능에 대한 기능 정의를 설정한다.
	 * @param authorityKey 권한 기능에 대한 보안 권한 키를 설정한다.
	 * @param create 생성 권한 소유 여부를 설정한다.
	 * @param read 조회/열람 권한 소유 여부를 설정한다.
	 * @param update 수정 권한 소유 여부를 설정한다.
	 * @param delete 삭제 권한 소유 여부를 설정한다.
	 * @return 새로 추가된 보안 권한 기능 정보가 반환된다.
	 */
	public SecurityAuthorityFunction insertSecurityAuthorityFunction(int functionId, int authorityId, boolean create, boolean read, boolean update, boolean delete)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria functionDefinitionsCriteria = session.createCriteria(FunctionDefinitions.class)
					.add(Restrictions.eq("functionId", functionId));
			
			FunctionDefinitions functionDefinition = (FunctionDefinitions)functionDefinitionsCriteria.uniqueResult();
			
			Criteria authorityCriteria = session.createCriteria(SecurityAuthority.class)
					.add(Restrictions.eq("authorityId", authorityId));
			
			SecurityAuthority authority = (SecurityAuthority)authorityCriteria.uniqueResult();
			
			SecurityAuthorityFunction authorityFunction = new SecurityAuthorityFunction(new SecurityAuthorityFunctionId(functionId, authorityId), authority, functionDefinition, create, read, update, delete);
			
			// 먼저 동일한 기능 키와 권한 키에 대한 보안 권한 기능을 삭제한다.
			//session.delete(authorityFunction);
			// TODO : 보안 권한 기능을 저장할 때 이전 권한을 어떻게 삭제할 지 고민해야 함.
			
			session.save(authorityFunction);
			
			return authorityFunction;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 기능 정의 목록을 조회한다.
	 * @return 기능 정의 목록이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<FunctionDefinitions> listFunctionDefinitions()
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(FunctionDefinitions.class);
			
			return criteria.list();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 접근 기록 목록을 조회한다.
	 * @param searchStartDateTime 접근 날짜/시각에 대한 검색 시작 조건을 설정한다.
	 * @param searchEndDateTime 접근 날짜/시각에 대한 검색 종료 조건을 설정한다.
	 * @return 보안 접근 목록이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<SecurityAccessTrace> listSecurityAccessTraces(Date searchStartDateTime, Date searchEndDateTime)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(SecurityAccessTrace.class)
					.add(Restrictions.between("AccessDateTime", searchStartDateTime, searchEndDateTime));
			
			return criteria.list();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 보안 접근 기록을 추가한다.
	 * @param identityId 보안 접근 기록의 보안 계정 아이디를 설정한다.
	 * @param accessType 접근 유형을 설정한다.
	 * @param remoteIpAddress 접근 계정 IP 주소를 설정한다.
	 * @param localIpAddress 접근 서버 IP 주소를 설정한다.
	 * @return
	 */
	public SecurityAccessTrace insertSecurityAccessTrace(Long identityId, short accessType, String remoteIpAddress, String localIpAddress)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria identityCriteria = session.createCriteria(SecurityIdentity.class)
					.add(Restrictions.eq("identityId", identityId));
			
			SecurityIdentity identity = (SecurityIdentity)identityCriteria.uniqueResult();
			
			SecurityAccessTrace accessTrace = new SecurityAccessTrace(new SecurityAccessTraceId(new Date(), identityId), identity, accessType, remoteIpAddress, localIpAddress);
			
			session.save(accessTrace);
			
			return accessTrace;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 관리 부서 목록을 조회한다.
	 * @param startLikeHierachicalLabel 계층 라벨에 대해 검색하기 위해 검색 파라메터를 설정한다.
	 * @return 조회된 관리 부서 목록이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<ManagementDepartment> listManagementDepartments(String startLikeHierachicalLabel)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			// ListManagementDepartmentHierachically 프로시저를 실행하기 위해 NamedQuery를 가져온다. 프로시저 쿼리 결과와 SecurityGroupHierachy 클래스를 매핑하기 위해 ResultTransformer의 구현을 설정한다.
			Query query = session.getNamedQuery("SecurityGroup.ListManagementDepartmentHierachically")
					.setString("StartLikeHierachicalLabel", startLikeHierachicalLabel)
					.setResultTransformer(new AutoCastableResultTransformer<ManagementDepartment>(ManagementDepartment.class)); 
			
			return query.list();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 전체 관리 부서 목록을 조회한다.
	 * @param startLikeHierachicalLabel 계층 라벨에 대해 검색하기 위해 검색 파라메터를 설정한다.
	 * @return 조회된 전체 관리 부서 목록이 반환된다.
	 */
	public List<ManagementDepartment> listManagementDepartments()
	{
		return this.listManagementDepartments("");
	}
	
	/**
	 * 관리 부서 정보를 저장한다.
	 * @param department 저장하려는 관리 부서 정보를 설정한다.
	 */
	public void saveManagementDepartment(ManagementDepartment department)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			session.saveOrUpdate(department);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 관리 부서 아이디에 대한 관리 부서를 삭제한다.
	 * @param departmentId 삭제하려는 관리 부서 아이디를 설정한다.
	 */
	public void deleteManagementDepartment(Long departmentId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			// 삭제하려는 보안 그룹의 하위 보안 그룹까지 모두 삭제하기 위한 프로시저를 실행한다.
			Query query = session.getNamedQuery("SecurityGroup.DeleteManagementDepartment")
					.setLong("DepartmentId", departmentId);
			
			query.executeUpdate();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 관리 부서에 대한 관리자 목록을 조회한다.
	 * @param departmentId 조회하려는 관리자 목록에 대한 소속 관리 부서 아이디를 설정한다.
	 * @return 주어진 관리 부서에 대한 관리자 목록이 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public List<ManagementMember> listManagementMembers(Long departmentId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria criteria = session.createCriteria(ManagementMember.class, "m")
					.createCriteria("m.managementDepartments", "d")
					.add(Restrictions.eq("d.departmentId", departmentId));
			
			return criteria.list();
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 관리자 정보를 저장한다.
	 * @param userName 관리자 이름을 설정한다.
	 * @param email 이메일 주소를 설정한다.
	 * @param level 직위를 설정한다.
	 * @param phoneNo 일반 전화 번호를 설정한다.
	 * @param cellNo 휴대 전화 번호를 설정한다.
	 * @param departmentsId 소속 관리 부서 아이디 목록을 설정한다.
	 * @return 저장된 관리자 정보가 반환된다.
	 */
	@SuppressWarnings("unchecked")
	public ManagementMember saveManagementMember(String userName, String email, String level, String phoneNo, String cellNo, List<Long> departmentsId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria departmentCriteria = session.createCriteria(ManagementDepartment.class)
					.add(Restrictions.in("departmentId", departmentsId));
			
			Set<ManagementDepartment> departments = new HashSet<>(departmentCriteria.list());
			
			ManagementMember member = new ManagementMember(userName, email, level, phoneNo, cellNo, new Date(), 0L, null, departments, null);
			
			session.saveOrUpdate(member);
			
			return member;
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 관리자 아이디에 대한 관리자 정보를 삭제한다.
	 * @param memberId 삭제하려는 관리자 아이디를 설정한다.
	 */
	public void deleteManagementMember(Integer memberId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria memberCriteria = session.createCriteria(ManagementMember.class)
					.add(Restrictions.eq("memberId", memberId));
			
			ManagementMember member = (ManagementMember)memberCriteria.uniqueResult();
			
			session.delete(member);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 관리자 아이디에 해당하는 관리자를 관리 부서에 소속시킨다.
	 * @param memberId 관리 부서에 소속시킬 관리자 아이디를 설정한다.
	 * @param departmentsId 관리자를 소속시킬 관리 부서 아이디 목록을 설정한다.
	 */
	public void insertMemberToDepartment(Integer memberId, List<Long> departmentsId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria memberCriteria = session.createCriteria(ManagementMember.class)
					.add(Restrictions.eq("memberId", memberId));
					
			ManagementMember member = (ManagementMember)memberCriteria.uniqueResult();
			
			for (int index = 0; index < departmentsId.size(); index++)
			{
				Long departmentId = departmentsId.get(index);
				
				Criteria departmentCriteria = session.createCriteria(ManagementDepartment.class)
						.add(Restrictions.eq("departmentId", departmentId));
				
				ManagementDepartment department = (ManagementDepartment)departmentCriteria.uniqueResult();
				
				member.getManagementDepartments().add(department);
			}
			
			session.save(member);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
	
	/**
	 * 주어진 관리자 아이디에 해당하는 관리자를 보안 계정에 소속시킨다.
	 * @param memberId 보안 계정에 소속시킬 관리자 아이디를 설정한다.
	 * @param identitiesId 관리자를 소속시킬 보안 계정 아이디 목록을 설정한다.
	 */
	public void insertMemberToIdentity(Integer memberId, List<Long> identitiesId)
	{
		Session session = this._sessionFactory.openSession();
		
		try
		{
			Criteria memberCriteria = session.createCriteria(ManagementMember.class)
					.add(Restrictions.eq("memberId", memberId));
			
			ManagementMember member = (ManagementMember)memberCriteria.uniqueResult();
			
			for (int index = 0; index < identitiesId.size(); index++)
			{
				Long identityId = identitiesId.get(index);
				
				Criteria identityCriteria = session.createCriteria(SecurityIdentity.class)
						.add(Restrictions.eq("identityId", identityId));
				
				SecurityIdentity identity = (SecurityIdentity)identityCriteria.uniqueResult();
				
				member.getSecurityIdentities().add(identity);
			}
			
			session.save(member);
		}
		finally
		{
			if (session != null && session.isOpen())
			{
				session.close();
			}
		}
	}
}